/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
 * MA  02110-1301, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */

package com.aionemu.gameserver.skillengine.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;

import org.apache.log4j.Logger;

import com.aionemu.gameserver.geoEngine.GeoEngine;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.gameobjects.instance.Summon;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionemu.gameserver.restrictions.RestrictionsManager;
import com.aionemu.gameserver.skillengine.effect.EffectId;
import com.aionemu.gameserver.skillengine.properties.FirstTargetAttribute;
import com.aionemu.gameserver.skillengine.properties.TargetRangeAttribute;
import com.aionemu.gameserver.skillengine.properties.TargetRelationAttribute;
import com.aionemu.gameserver.utils.MathUtil;
import com.aionemu.gameserver.utils.PacketSendUtility;

/**
 * @author Mr. Poke
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "TargetProperty")
public class TargetProperty
{

	private static final Logger			log				= Logger.getLogger(TargetProperty.class);

	@XmlAttribute
	protected int						maxcount;
	@XmlAttribute
	protected int						distance;
	@XmlAttribute
	protected int						radius;
	@XmlAttribute(name = "target_range")
	protected TargetRangeAttribute		targetRange = TargetRangeAttribute.NONE;
	@XmlAttribute(name = "target_relation")
	protected TargetRelationAttribute	targetRelation	= TargetRelationAttribute.NONE;
	@XmlAttribute(name = "first_target_range")
	protected int						firstTargetRange;
	@XmlAttribute(name = "first_target_attribute")
	protected FirstTargetAttribute		firstTargetAttribute;
	@XmlAttribute(name = "add_weapon_range")
	protected Boolean					addWeaponRange	= false;

	public boolean calc(Skill skill)
	{
		
		if ((skill.getEffector().getEffectController().isAbnormalState(EffectId.CANT_ATTACK_STATE)) && (skill.getSkillTemplate().getSkillId() != 1968))
			return false;
		
		switch (firstTargetAttribute)
		{
			case ME:
				skill.setFirstTarget(skill.getEffector());
				skill.setFirstTargetRangeCheck(false);
				break;
			case TARGETORME:
				if (skill.getFirstTarget() == null)
				{
					skill.setFirstTarget(skill.getEffector());
					skill.setFirstTargetRangeCheck(false);
					break;
				}
				switch (targetRelation)
				{
					case ALL:
						break;
					case ENEMY:
						if (!skill.getEffector().isEnemy(skill.getFirstTarget()))
							return false;
						break;
					case FRIEND:
						if (skill.getEffector().isEnemy(skill.getFirstTarget()))
						{
							skill.setFirstTarget(skill.getEffector());
							skill.setFirstTargetRangeCheck(false);
							if (skill.getEffector() instanceof Player)
							{
								PacketSendUtility.sendPacket((Player) skill.getEffector(), SM_SYSTEM_MESSAGE.STR_SKILL_AUTO_CHANGE_TARGET_TO_MY());
							}
						}
						break;
					case MYPARTY:
						if (!(skill.getFirstTarget() instanceof Player) || !(skill.getEffector() instanceof Player)
								|| !((Player) skill.getFirstTarget()).isInGroup() || !((Player) skill.getEffector()).isInGroup()
								|| ((Player) skill.getFirstTarget()).getPlayerGroup() != ((Player) skill.getEffector()).getPlayerGroup()
								)
						{
							skill.setFirstTarget(skill.getEffector());
							skill.setFirstTargetRangeCheck(false);
						}
						
					case NONE:
						break;

				}
				break;
			case TARGET:
				if (skill.getFirstTarget() == null)
					return false;
				break;
			case MYPET:
				Creature effector = skill.getEffector();
				if (effector instanceof Player)
				{
					Summon summon = ((Player) effector).getSummon();
					if (summon != null)
						skill.setFirstTarget(summon);
					else
						return false;
				}
				else
				{
					return false;
				}
				break;
			case PASSIVE:
				skill.setFirstTarget(skill.getEffector());
				skill.setFirstTargetRangeCheck(false);
				break;
			case POINT:
				// TODO: Implement Range Check for Point 
				skill.setFirstTargetRangeCheck(false);
				break;
		}

		Creature effector = skill.getEffector();
		Creature firstTarget = skill.getFirstTarget();

		if (skill.isFirstTargetRangeCheck())
		{
			if (firstTarget == null)
				return false;
			float firstRange = effector.getBoundingRadius().getCollision() + firstTarget.getBoundingRadius().getCollision() + firstTargetRange;
			if (addWeaponRange)
				firstRange += (effector.getGameStats().getAttackRange().getCurrent() / 1000f);
			if (effector.getMoveData().moveStartTime > 0)
				firstRange += 1;
			if (firstTarget.getMoveData().moveStartTime > 0)
				firstRange += 1;
			//TODO
			firstRange += 1;

			skill.setFirstTargetRange(firstRange);
			if (!MathUtil.isIn3dRange(effector, firstTarget, firstRange))
			{
				if (effector instanceof Player)
				{
					PacketSendUtility.sendPacket((Player) effector, SM_SYSTEM_MESSAGE.STR_ATTACK_TOO_FAR_FROM_TARGET());
				}
				return false;
			}

			if (!GeoEngine.getInstance().canSee(effector, firstTarget))
			{
				if (effector instanceof Player)
					PacketSendUtility.sendPacket((Player) effector, SM_SYSTEM_MESSAGE.STR_SKILL_OBSTACLE);
				return false;
			}
		}

		switch (targetRange)
		{
			case AREA:
				if (firstTarget == null)
				{
					log.warn("CHECKPOINT: first target is null for skillid " + skill.getSkillTemplate().getSkillId());
					return false;
				}
				switch (targetRelation)
				{
					case ENEMY:
						if (skill.getEffector().isEnemy(firstTarget) && GeoEngine.getInstance().canSee(skill.getEffector(), firstTarget))
							skill.getEffectedList().add(firstTarget);
						break;
					case FRIEND:
						if (!skill.getEffector().isEnemy(firstTarget)&& GeoEngine.getInstance().canSee(skill.getEffector(), firstTarget))
							skill.getEffectedList().add(firstTarget);
						break;
					case MYPARTY:
						if (firstTarget instanceof Player && skill.getEffector() instanceof Player
								&& ((Player) firstTarget).isInGroup() && ((Player) skill.getEffector()).isInGroup()
								&& ((Player) firstTarget).getPlayerGroup() == ((Player) skill.getEffector()).getPlayerGroup()&& GeoEngine.getInstance().canSee(skill.getEffector(), firstTarget))
							skill.getEffectedList().add(firstTarget);
						break;
				}
				for (VisibleObject nextCreature : skill.getFirstTarget().getKnownList().getKnownObjects().values())
				{
					if (maxcount > 0 && skill.getEffectedList().size() >= maxcount)
						return true;
						
					if (!(nextCreature instanceof Creature))
						continue;
					Creature creature = (Creature)nextCreature;
					
					if (skill.getSkillTemplate().hasResurrectEffect() && !creature.getLifeStats().isAlreadyDead())
						continue;
					else if (!skill.getSkillTemplate().hasResurrectEffect() && creature.getLifeStats().isAlreadyDead())
						continue;
					if (effector instanceof Player && !RestrictionsManager.canAffectBySkill((Player) effector, creature))
						continue;
					switch (targetRelation)
					{
						case ENEMY:
							if (!skill.getEffector().isEnemy(creature))
								continue;
							break;
						case FRIEND:
							if (skill.getEffector().isEnemy(creature))
								continue;
							break;
						case MYPARTY:
							if (!(creature instanceof Player) || !(skill.getEffector() instanceof Player)
									|| !((Player) creature).isInGroup() || !((Player) skill.getEffector()).isInGroup()
									|| ((Player) creature).getPlayerGroup() != ((Player) skill.getEffector()).getPlayerGroup())
								continue;
							break;
					}
					float range = creature.getBoundingRadius().getCollision() + firstTarget.getBoundingRadius().getCollision();
					if (MathUtil.isIn3dRange(firstTarget, creature, range + distance) && !skill.getEffectedList().contains(creature) && GeoEngine.getInstance().canSee(creature, firstTarget))
						skill.getEffectedList().add(creature);
				}
				return true;
			case PARTY:
			case PARTY_WITHPET:
				if (skill.getEffector() instanceof Player)
				{
					Player player = (Player) skill.getEffector();
					if (player.isInGroup())
					{
						for (Player member : player.getPlayerGroup().getOnlineMembers())
						{
							float range = effector.getBoundingRadius().getCollision() + member.getBoundingRadius().getCollision();
							if (member != null && MathUtil.isIn3dRange(effector, member, range + distance)
									&& RestrictionsManager.canAffectBySkill(player, member)
									&& !skill.getEffectedList().contains(effector));//TODO && GeoEngine.getInstance().canSee(skill.getEffector(), member))
								skill.getEffectedList().add(member);
							if (maxcount > 0 && skill.getEffectedList().size() >= maxcount)
								return true;
							if (targetRange == TargetRangeAttribute.PARTY )
							{
								if (member != null && member.getSummon() != null
										&& MathUtil.isIn3dRange(effector, member.getSummon(), range + distance)
										&& RestrictionsManager.canAffectBySkill((Player) effector, member.getSummon())
										&& !skill.getEffectedList().contains(effector)); //TODO && GeoEngine.getInstance().canSee(skill.getEffector(), member.getSummon()))
									skill.getEffectedList().add(member.getSummon());
								if (maxcount > 0 && skill.getEffectedList().size() >= maxcount)
									return true;
							}
						}
					}
					else if (!skill.getEffectedList().contains(effector))
						skill.getEffectedList().add(effector);
				}
				return true;
			case ONLYONE:
			case POINT:
			case NONE:
				if (effector instanceof Player && !RestrictionsManager.canAffectBySkill((Player) effector, skill.getFirstTarget()))
					return false;
				skill.getEffectedList().add(skill.getFirstTarget());
		}
		return true;
	}

}
